﻿/*************************************************************************
 *  Copyright © 2023-2030 Administrator. All rights reserved.
 *------------------------------------------------------------------------
 *  公司：DefaultCompany
 *  项目：CustomPackageManager
 *  文件：SVNHelper.cs
 *  作者：Administrator
 *  日期：2024/9/28 22:10:41
 *  功能：Nothing
*************************************************************************/

using System.Threading.Tasks;
using System.Linq;
using System;
using System.Text;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Text.RegularExpressions;

namespace SVNPackageManager.Runtime
{
    // 进度更新事件参数
    public class SVNProgressEventArgs : EventArgs
    {
        public int TotalFiles { get; set; } // 总文件数
        public int CompletedFiles { get; set; } // 已完成文件数
        public string CurrentFile { get; set; } // 当前文件路径
    }

    // 进度委托
    public delegate void SVNProgressEventHandler(object sender, SVNProgressEventArgs args);

    [System.Serializable]
    public class SVNHelper
    {
        public string username;
        public string password;
        public string svnPath;

        public SVNHelper(PackageResourceManage packageResource)
        {
            this.username = packageResource.svnScopedRegistryConfig.username;
            this.password = packageResource.svnScopedRegistryConfig.password;

            svnPath = PackagePathManage.SVNClientPath;
        }

        private ProcessStartInfo CreateProcessStartInfo(string args)
        {
            var commandArgs = new List<string>();

            if (!string.IsNullOrEmpty(username))
                commandArgs.Add($"--username \"{username}\"");

            // 使用 --non-interactive 避免密码提示
            if (!string.IsNullOrEmpty(password))
                commandArgs.Add($"--password \"{password}\" --non-interactive");

            commandArgs.Add(args);

            if (!File.Exists(svnPath))
                throw new FileNotFoundException($"SVN client not found at: {svnPath}");

            var startInfo = new ProcessStartInfo
            {
                FileName = svnPath,
                Arguments = string.Join(" ", commandArgs),
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
            };

            return startInfo;
        }

        /// <summary>
        /// 安全执行 SVN 命令
        /// </summary>
        public async Task<string> ExecuteSVNCommandAsync(string args, CancellationToken ct = default)
        {
            var startInfo = CreateProcessStartInfo(args);

            var output = new StringBuilder();
            var errorOutput = new StringBuilder();

            using (var process = new Process { StartInfo = startInfo })
            {
                process.Start();
                // 同步读取输出流（避免异步事件丢失数据）
                using (var outputReader = process.StandardOutput)
                using (var errorReader = process.StandardError)
                {
                    // 读取输出和错误（同步阻塞）
                    string outputText = await outputReader.ReadToEndAsync();
                    string errorText = await errorReader.ReadToEndAsync();

                    output.Append(outputText);
                    errorOutput.Append(errorText);
                }

                await process.WaitForExitAsync(ct);

                if (process.ExitCode != 0)
                {
                    throw new InvalidOperationException(
                        $"SVN command failed with code {process.ExitCode}. Error: {errorOutput}"
                    );
                }

                return output.ToString().Trim();
            }
        }

        public async Task ExecuteSVNCheckOutCommandAsync(string args, SVNProgressEventHandler progressHandler, SVNProgressEventArgs progressArgs, CancellationToken ct = default)
        {
            var startInfo = CreateProcessStartInfo(args);

            string output;
            int completedFiles = 0;

            using (var process = new Process { StartInfo = startInfo })
            {
                process.Start();

                string pattern = @"^A {1,}(\S+)";
                Regex regex = new Regex(pattern);

                while ((output = await process.StandardOutput.ReadLineAsync()) != null)
                {
                    if (ct.IsCancellationRequested)
                    {
                        process.Kill();
                        throw new OperationCanceledException("检出已取消。");
                    }

                    Match match = regex.Match(output);
                    if (match.Success)
                    {
                        completedFiles++;
                        if (progressHandler != null)
                        {
                            progressArgs.CompletedFiles = completedFiles;
                            progressArgs.CurrentFile = match.Groups[1].Value;
                            progressHandler(this, progressArgs); // 触发进度更新
                        }
                    }
                }

                var errorOutput = new StringBuilder();
                using (var errorReader = process.StandardError)
                {
                    // 读取输出和错误（同步阻塞）
                    string errorText = await errorReader.ReadToEndAsync();

                    errorOutput.Append(errorText);
                }

                await process.WaitForExitAsync(ct);

                if (process.ExitCode != 0)
                {
                    throw new InvalidOperationException(
                        $"SVN command failed with code {process.ExitCode}. Error: {errorOutput}"
                    );
                }
            }
        }

        public async Task<int> GetSVNFileCountAsync(string svnUrl, CancellationToken ct = default)
        {
            // 使用 svn list 递归列出所有文件和文件夹
            string output = await ExecuteSVNCommandAsync($"list \"{svnUrl}\" --recursive", ct: ct);
            var entries = output.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            return entries.Length;
        }

        /// <summary>
        /// 获取所有包含 package.json 的包列表
        /// </summary>
        public async Task<List<string>> GetSVNListAsync(string svnUrl, CancellationToken ct = default)
        {
            try
            {
                // 使用 svn list 递归列出所有文件和文件夹
                string output = await ExecuteSVNCommandAsync($"list \"{svnUrl}\" --depth infinity", ct: ct);
                var entries = output.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                entries = entries.Select(item => item.Trim()).ToArray();

                // 过滤出包含 package.json 的文件夹路径
                var packages = new HashSet<string>();
                foreach (var entry in entries)
                {
                    if (ct.IsCancellationRequested)
                        break;

                    // 如果条目是 package.json，提取其所在文件夹
                    if (entry.EndsWith("package.json", StringComparison.OrdinalIgnoreCase))
                    {
                        string packageUrl = entry.Substring(0, entry.Length - "package.json".Length).TrimEnd('/');
                        string packageSvnUrl = $"{svnUrl}/{packageUrl}";
                        packages.Add(packageSvnUrl);
                    }
                }

                return packages.ToList();
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.LogError($"获取包列表失败: {ex.Message}");
                throw;
            }
        }


        /// <summary>
        /// 获取文件内容
        /// </summary>
        public async Task<string> GetSVNFileContentAsync(string packageUrl, string fileName, CancellationToken ct = default)
        {
            string svnUrl = $"{packageUrl}/{fileName}";

            return await ExecuteSVNCommandAsync($"cat \"{svnUrl}\"", ct: ct);
        }

        /// <summary>
        /// 获取json文件内容
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="packageUrl"></param>
        /// <param name="fileName"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public async Task<T> GetSVNJsonFileContentAsync<T>(string packageUrl, string fileName = "package.json", CancellationToken ct = default)
        {
            string jsonContent = await GetSVNFileContentAsync(packageUrl, fileName, ct);
            try
            {
                return JsonConvert.DeserializeObject<T>(jsonContent);
            }
            catch (JsonException ex)
            {
                UnityEngine.Debug.LogError($"JSON解析失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 异步检出目录
        /// </summary>
        public async Task CheckoutFolderAsync(string packageUrl, string localPath, SVNProgressEventHandler progressHandler, CancellationToken ct = default)
        {
            if (string.IsNullOrEmpty(packageUrl))
                throw new ArgumentException("package Url cannot be empty.");

            if (string.IsNullOrEmpty(localPath))
                throw new ArgumentException("Local path cannot be empty.");

            string svnUrl = $"{packageUrl}";
            string args = $"checkout \"{svnUrl}\" \"{localPath}\"";

            try
            {
                int count = await GetSVNFileCountAsync(svnUrl, ct);
                SVNProgressEventArgs progressArgs = new SVNProgressEventArgs();
                progressArgs.TotalFiles = count;

                await ExecuteSVNCheckOutCommandAsync(args, progressHandler, progressArgs, ct);
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.LogError($"检出失败: {ex.Message}");
                throw;
            }
        }
    }

    // Process 扩展方法支持 async/await
    public static class ProcessExtensions
    {
        public static Task WaitForExitAsync(this Process process, CancellationToken ct = default)
        {
            var tcs = new TaskCompletionSource<bool>();
            process.EnableRaisingEvents = true;
            process.Exited += (sender, args) => tcs.TrySetResult(true);
            ct.Register(() => tcs.TrySetCanceled());
            return tcs.Task;
        }
    }
}

